package com.example.generator.service;

import com.example.generator.model.TableInfo;
import com.example.generator.config.GeneratorConfig;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class CodeGeneratorService {

    private static final Logger log = LoggerFactory.getLogger(CodeGeneratorService.class);

    @Autowired
    private Configuration freemarkerConfig;

    @Autowired
    private GeneratorConfig config;

    @Autowired
    private DatabaseService databaseService;
    
    /**
     * 生成代码并打包成ZIP
     *
     * @param tableName 表名
     * @return ZIP文件路径
     */
    public String generateCode(String tableName, String author, String basePackage) throws Exception {
        String tempDir = "temp/" + System.currentTimeMillis();
        String zipFile = tempDir + "/" + tableName + "_code.zip";
        try {
            // 创建临时目录
            new File(tempDir).mkdirs();
            
            // 生成代码
            generateTableCode(tableName, author, basePackage, tempDir);
            
            // 创建ZIP文件
            try (FileOutputStream fos = new FileOutputStream(zipFile);
                 ZipOutputStream zos = new ZipOutputStream(fos)) {
                File sourceFile = new File(tempDir);
                addToZip(sourceFile, sourceFile.getName(), zos);
            }
            
            return zipFile;
        } catch (Exception e) {
            // 清理临时文件
            deleteDirectory(new File(tempDir));
            log.error("代码生成失败", e);
            throw new RuntimeException("代码生成失败", e);
        }
    }
    
    /**
     * 生成代码到指定目录
     */
    public void generateTableCode(String tableName, String author, String basePackage, String outputPath) throws Exception {
        try {
            // 保存原始输出路径
            String originalOutputPath = config.getOutputPath();
            
            // 设置新的输出路径
            config.setOutputPath(outputPath);
            
            // 获取表信息
            TableInfo tableInfo = databaseService.getTableInfo(tableName);
            config.setAuthor(author);
            config.setBasePackage(basePackage);
            
            // 生成所有代码文件
            generateEntity(tableInfo);
            generateDao(tableInfo);
            generateMapper(tableInfo);
            generateMapperXml(tableInfo);
            // generateService(tableInfo);
            // generateServiceImpl(tableInfo);
            generateEntityTest(tableInfo);
            // generateMapperTest(tableInfo);
            
            // 恢复原始输出路径
            config.setOutputPath(originalOutputPath);
        } catch (Exception e) {
            log.error("代码生成失败", e);
            throw new RuntimeException("代码生成失败", e);
        }
    }
    
    /**
     * 生成Mapper测试类
     */
    private void generateMapperTest(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "MapperTest.java";
        String packagePath = config.getBasePackage() + ".mapper";
        
        // 保存原始输出路径
        String originalOutputPath = config.getOutputPath();
        
        try {
            // 使用配置的测试目录路径
            config.setOutputPath(config.getTestOutputPath());
            generateFile("mapperTest.ftl", dataModel, packagePath, fileName);
        } finally {
            // 恢复原始输出路径
            config.setOutputPath(originalOutputPath);
        }
    }
    
    /**
     * 递归添加文件到ZIP
     */
    private void addToZip(File file, String path, ZipOutputStream zos) throws IOException {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File child : files) {
                    addToZip(child, path + "/" + child.getName(), zos);
                }
            }
        } else if (!file.getName().endsWith(".zip")) {
            try (FileInputStream fis = new FileInputStream(file)) {
                ZipEntry zipEntry = new ZipEntry(path);
                zos.putNextEntry(zipEntry);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, length);
                }
                zos.closeEntry();
            }
        }
    }
    
    /**
     * 删除目录及其内容
     */
    private void deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
            directory.delete();
        }
    }
    
    /**
     * 生成实体类
     */
    private void generateEntity(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        // 生成 19 位随机数
        Random random = new Random();
        // 定义 19 位正数的最小值和最大值
        long min = 1000000000000000000L;
        long max = Long.MAX_VALUE;
        // 先生成 19 位正数
        long randomNumber = min + (long) (random.nextDouble() * (max - min));
        
        // 随机决定是否添加负号
        if (random.nextBoolean()) {
            randomNumber = -randomNumber;
        }
        dataModel.put("serialVersionUID", randomNumber);
        String fileName = tableInfo.getClassName() + "Entity.java";
        String packagePath = config.getBasePackage() + ".model.entity";
        generateFile("entity.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成Dao接口
     */
    private void generateDao(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "Dao.java";
        String packagePath = config.getBasePackage() + ".dao";
        generateFile("dao.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成Mapper接口
     */
    private void generateMapper(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "Mapper.java";
        String packagePath = config.getBasePackage() + ".mapper";
        generateFile("mapper.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成Mapper XML文件
     */
    private void generateMapperXml(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "Mapper.xml";
        String packagePath = "mapper";
        generateFile("mapperXml.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成Service接口
     */
    private void generateService(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "Service.java";
        String packagePath = config.getBasePackage() + ".service";
        generateFile("service.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成Service实现类
     */
    private void generateServiceImpl(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "ServiceImpl.java";
        String packagePath = config.getBasePackage() + ".service.impl";
        generateFile("serviceImpl.ftl", dataModel, packagePath, fileName);
    }
    
    /**
     * 生成单元测试类
     */
    public void generateEntityTest(TableInfo tableInfo) throws Exception {
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("table", tableInfo);
        dataModel.put("config", config);
        String fileName = tableInfo.getClassName() + "EntityTest.java";
        String packagePath = config.getBasePackage() + ".model.test";
        
        // 保存原始输出路径
        String originalOutputPath = config.getTestOutputPath();
        
        try {
            // 使用配置的测试目录路径
            config.setOutputPath(config.getTestOutputPath());
            generateFile("entityTest.ftl", dataModel, packagePath, fileName);
        } finally {
            // 恢复原始输出路径
            config.setOutputPath(originalOutputPath);
        }
    }
    
    /**
     * 生成文件的通用方法
     */
    private void generateFile(String templateName, Map<String, Object> dataModel,
                              String packagePath, String fileName) throws Exception {
        Template template = freemarkerConfig.getTemplate(templateName);
        // 创建目录
        String outputPath = config.getOutputPath() + "/" + packagePath.replace('.', '/');
        File dir = new File(outputPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        // 生成文件
        String filePath = outputPath + "/" + fileName;
        try (Writer writer = new FileWriter(filePath)) {
            template.process(dataModel, writer);
        }
        log.info("Generated file: {}", filePath);
    }
    
    /**
     * 生成代码到文件夹
     *
     * @param tableName 表名
     * @return 文件夹路径
     */
    public String generateCodeAsFolder(String tableName, String author, String basePackage) throws Exception {
        String outputPath = "temp/" + System.currentTimeMillis() + "/" + tableName;
        try {
            // 创建输出目录
            new File(outputPath).mkdirs();
            
            // 生成代码
            generateTableCode(tableName, author, basePackage, outputPath);
            
            return outputPath;
        } catch (Exception e) {
            // 清理临时文件
            deleteDirectory(new File(outputPath));
            log.error("代码生成失败", e);
            throw new RuntimeException("代码生成失败", e);
        }
    }
}
